package com.tianji.service.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.tianji.api.dto.trade.PrePlaceOrder;
import com.tianji.common.utils.UserContext;
import com.tianji.config.SpringAiConfig;
import com.tianji.domain.dto.ChatDto;
import com.tianji.domain.vo.ChatVo;
import com.tianji.enums.ChatEventTypeEnum;
import com.tianji.service.ChatService;
import com.tianji.service.ChatSessionService;
import com.tianji.tools.CourseInfo;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;

import static com.tianji.tools.CourseTool.TOOL_CONTEXT_PREFIX;
import static com.tianji.tools.OrderTool.TRADE_PRE_KEY;

/**
 * @author Administrator
 */
@Service
@Slf4j
public class ChatServiceImpl implements ChatService {
    @Autowired
    private ChatClient chatClient;
    private static final Map<String, Boolean> SESSION_FLAG = new ConcurrentHashMap<>();
    @Autowired
    private ChatMemory chatMemory;
    @Autowired
    private StringRedisTemplate redisTemplate;
 @Autowired
 private ChatSessionService chatSessionService;
    @Override
    public Flux<ChatVo> getModelReply(ChatDto dto) {
        StringBuilder sb = new StringBuilder();
        String conversionId = UserContext.getUser() + ":" + dto.getSessionId();
        String requestId = IdUtil.fastSimpleUUID();
        Long userId = UserContext.getUser();
        chatSessionService.updateChatSessionTittle(dto.getSessionId(), dto.getQuestion(),userId);
        Map<String, Object> map = new HashMap<>();
        map.put("requestId", requestId);
        map.put("userId",userId );
        log.info("redis中的requestId：{}", requestId);
        return chatClient.prompt()
                .system(SpringAiConfig.AI_PROMPT)
                .advisors(a -> a.param(AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY, conversionId))
                .user(dto.getQuestion())
                .toolContext(map)
                .stream()
                .chatResponse()
                .doFirst(() -> {
                    log.info("开始会话之前：{}", dto.getSessionId());
                    SESSION_FLAG.put(dto.getSessionId(), true);
                })
                .doOnComplete(() -> {
                    log.info("结束之后做的事情：{}", dto.getSessionId());
                    SESSION_FLAG.remove(dto.getSessionId());
                })
                .doOnError(throwable -> {
                    log.info("出现异常时做的事情：{}", dto.getSessionId());
                    SESSION_FLAG.remove(dto.getSessionId());
                })
                .doOnCancel(new Runnable() {
                    @Override
                    public void run() {
                        log.info("取消之后做的事情：{}", dto.getSessionId());
                        chatMemory.add(conversionId, new AssistantMessage(sb.toString()));
                    }
                })
                .takeWhile(chatResponse -> SESSION_FLAG.getOrDefault(dto.getSessionId(), false))
                .map(chatResponse -> {
                    sb.append(chatResponse.getResult().getOutput().getText());
                    return ChatVo.builder().eventData(chatResponse.getResult().getOutput().getText())
                            .eventType(ChatEventTypeEnum.DATA.getValue())
                            .build();
                })
                .concatWith(Flux.defer(new Supplier<Publisher<ChatVo>>() {
                    @Override
                    public Publisher<ChatVo> get() {
                        String courseInfoJson = (String) redisTemplate.opsForHash().get(TOOL_CONTEXT_PREFIX, requestId);
                        if (courseInfoJson!=null){
                            redisTemplate.opsForHash().delete(TOOL_CONTEXT_PREFIX, requestId);
                            CourseInfo bean = JSONUtil.toBean(courseInfoJson, CourseInfo.class);
                            Map<String, Object> toolMap = new HashMap<>();
                            toolMap.put("courseInfo_"+bean.getId(), bean);
                            return Flux.just(ChatVo.builder().eventData(toolMap).eventType(ChatEventTypeEnum.PARAM.getValue()).build()
                                    ,ChatVo.builder().eventType(ChatEventTypeEnum.STOP.getValue()).build());
                        }
                        String prePlaceOrderJson = (String) redisTemplate.opsForHash().get(TRADE_PRE_KEY, userId.toString());
                        if  (StrUtil.isNotBlank(prePlaceOrderJson)){
                            redisTemplate.opsForHash().delete(TRADE_PRE_KEY, userId.toString());
                            PrePlaceOrder prePlaceOrder = JSONUtil.toBean(prePlaceOrderJson, PrePlaceOrder.class);
                            Map<String, Object> prePlaceOrderMap = new HashMap<>();
                            prePlaceOrderMap .put("prePlaceOrder", prePlaceOrder);

                            return Flux.defer(new Supplier<Publisher<ChatVo>>() {
                                @Override
                                public Publisher<ChatVo> get() {
                                    return Flux.just(ChatVo.builder().eventData(prePlaceOrderMap )
                                            .eventType(ChatEventTypeEnum.PARAM.getValue())
                                            .build(),ChatVo.builder().eventType(ChatEventTypeEnum.STOP.getValue()).build());
                                }
                            });
                        }
                        return Flux.just(ChatVo.builder().eventType(ChatEventTypeEnum.STOP.getValue()).build());
                    }


                }));
    }

    @Override
    public void stopSession(String sessionId) {
        SESSION_FLAG.remove(sessionId);
    }
    @Autowired
    private ChatClient chatClient1;
    @Override
    public String getReplayText(String question) {
       return chatClient1.prompt()
               .system(SpringAiConfig.TEXT_ROLE)
               .user(question).call().content();
    }
}
